package com.ts.api.module.api.handler;

import com.gitee.apanlh.util.base.CollUtils;
import com.gitee.apanlh.util.base.Eq;
import com.gitee.apanlh.util.date.DateUtils;
import com.gitee.apanlh.util.valid.Assert;
import com.gitee.apanlh.util.valid.ValidParam;
import com.ts.api.common.constant.api.ApiBizTypeEnum;
import com.ts.api.common.constant.api.ApiEnum;
import com.ts.api.common.constant.productflow.ProductApplyFlowStatusEnum;
import com.ts.api.common.context.ApiContext;
import com.ts.api.common.redis.DataExchangeRedisKey;
import com.ts.api.exp.ApiMsgException;
import com.ts.api.module.api.entity.ApiUserRequest;
import com.ts.api.module.api.entity.bo.ApiBizMatchBO;
import com.ts.api.module.api.entity.bo.ApiCheckHistoryUserBO;
import com.ts.api.module.api.entity.bo.ApiParameterBO;
import com.ts.api.module.api.entity.bo.ApiWakeBO;
import com.ts.api.module.api.entity.po.ApiKeyPO;
import com.ts.api.module.api.entity.vo.ReqApiParameterVO;
import com.ts.api.module.api.logger.ApiFlowLogger;
import com.ts.api.module.api.service.ApiBizRequiredService;
import com.ts.api.module.api.wake.WakeService;
import com.ts.api.module.product.entity.po.ProductApplyFlowPO;
import com.ts.api.module.product.entity.po.ProductPlanPO;
import com.ts.api.module.product.service.ProductApplyFlowService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 *  API渠道撞库处理器
 *
 *  @author Pan
 */
@Component
public class ApiDataExchangeHandler {

    @Autowired
    private ApiBizRequiredService apiBizRequiredService;
    @Autowired
    private WakeService wakeService;
    @Autowired
    private ProductApplyFlowService productApplyFlowService;

    /**
     *  撞库限流
     *
     *  @param key  键
     */
    public void handleLimitMd5(String key) {
        //  如果出现并发时将此次流程放弃，抛出异常
        if (DateUtils.timeCompareTo(9) >= 0 && DateUtils.timeCompareTo(23) <= 0) {
            boolean absent = DataExchangeRedisKey.PHONE_MD5.setIfAbsentForSeconds(key, "1", 2L);
            Assert.isTrueThrows(absent, new ApiMsgException(ApiEnum.API_DATA_EXCHANGE_DISCARD));
        } else {
            boolean absent = DataExchangeRedisKey.PHONE_MD5.setIfAbsentForSeconds(key, "1", 5L);
            Assert.isTrueThrows(absent, new ApiMsgException(ApiEnum.API_DATA_EXCHANGE_DISCARD));
        }
    }

    /**
     *  是否用户存在
     *
     *  @param apiParameterBO
     */
    public void handleLocalExist(ApiParameterBO apiParameterBO) {
        ApiKeyPO apiKeyPO = apiParameterBO.getApiKeyPO();
        ReqApiParameterVO reqApiParameterVO = apiParameterBO.getReqApiParameterVO();
        ApiUserRequest apiUserRequest = reqApiParameterVO.getApiUserRequest();
        String phoneMd5 = apiUserRequest.getPhoneMd5();

        //  过滤用户分发
        ApiCheckHistoryUserBO apiCheckHistoryUserBO = ApiCheckHistoryUserBO.createApiKeyByMd5(apiKeyPO, phoneMd5);
        boolean existHistoryUser = apiBizRequiredService.existHistoryUser(apiCheckHistoryUserBO);

        //  唤醒流程
        ApiBizMatchBO apiBizMatchBO = apiParameterBO.getApiBizMatchBO();
        if (apiBizMatchBO.getEnableWake()) {
            String reqId = ApiContext.REQ_ID.get();
            ApiWakeBO apiWakeBO = new ApiWakeBO();
            apiWakeBO.setApiKeyPO(apiKeyPO);
            apiWakeBO.setPhoneMd5(phoneMd5);
            apiWakeBO.setReqId(reqId);

            boolean hasLocalExist = apiParameterBO.getHasLocalExist();
            if (hasLocalExist) {
                apiWakeBO.setHasLocalExist(true);
            }
            if (existHistoryUser) {
                apiWakeBO.setHasHistoryExist(true);
            }
            if (hasLocalExist || existHistoryUser) {
                wakeService.wake(apiWakeBO, apiBizMatchBO);
                throw new ApiMsgException(ApiEnum.API_USER_INFO_EXIST.format(ApiBizTypeEnum.DATA_EXCHANGE.getName()));
            }
        }

        //  如果存在直接抛出异常
        Assert.isFalseThrows(existHistoryUser, new ApiMsgException(ApiEnum.API_DATA_EXCHANGE_FILTER_USER));
    }

    /**
     *  检测当前渠道与匹配计划是否一致性
     *  如果当前渠道匹配的下游与上次不一样则将原先匹配到的流程数据放弃，并插入新流程数据
     *  如果匹配一致则无须插入流程记录
     *
     *  @param  apiParameterBO      API业务参数实体
     *  @param  finalMatchPlanPO    最终匹配到的计划
     *  @param  checkMark           检测流程标识(PhoneMd5,或者掩码md5)
     *  @return boolean             返回是否插入新的流程标识
     */
    public void checkMatchConsistency(ApiParameterBO apiParameterBO, ProductPlanPO finalMatchPlanPO, String checkMark) {
        ApiBizMatchBO apiBizMatchBO = apiParameterBO.getApiBizMatchBO();
        ApiKeyPO apiKeyPO = apiParameterBO.getApiKeyPO();
        String apiClientId = apiKeyPO.getApiClientId();

        ReqApiParameterVO reqApiParameterVO = apiParameterBO.getReqApiParameterVO();
        ApiUserRequest apiUserRequest = reqApiParameterVO.getApiUserRequest();
        String city = apiUserRequest.getCity();

        //  根据当前渠道查询以往流程中所属关联机构计划
        List<ProductApplyFlowPO> listFlow;
        //  特殊计划匹配不参加-业务流程处理
        if (apiBizMatchBO.hasOnlySpecialPlan()) {
            listFlow = null;
        } else {
            listFlow = apiBizRequiredService.existFailFlow(apiClientId, checkMark);
        }

        ProductApplyFlowPO findDataExchangeSucFlow = CollUtils.findOne(listFlow, t -> Eq.object(ProductApplyFlowStatusEnum.CHANNEL_DATA_EXCHANGE.getType(), t.getStatus()));
        boolean existFlow = ValidParam.isNotNull(findDataExchangeSucFlow);

        //  如果当前匹配计划与原来匹配计划的不一致
        if (existFlow && !Eq.objectOr(finalMatchPlanPO.getId(), findDataExchangeSucFlow.getLoanProductPlanId())) {
            //  获取当前客户端所有流程
            List<ProductApplyFlowPO> listFlowByClientId = CollUtils.find(listFlow, t -> Eq.str(t.getApiClientId(), apiClientId));

            //  将原来的流程变更为过期流程,保证能够正常匹配-过期状态-渠道-撞库成功
            String updateTime = DateUtils.currentTime();
            ProductApplyFlowPO findDataExchangeSuc = CollUtils.findOne(listFlowByClientId, t -> Eq.objectOr(t.getStatus(), ProductApplyFlowStatusEnum.CHANNEL_DATA_EXCHANGE.getType()));
            if (ValidParam.isNotNull(findDataExchangeSuc)) {
                findDataExchangeSuc.setUpdateTime(updateTime);
                findDataExchangeSuc.setStatus(ProductApplyFlowStatusEnum.EXPIRE_CHANNEL_DATA_EXCHANGE.getType());
                productApplyFlowService.updateFlowStatusById(findDataExchangeSuc);
            }
            //  将原来的流程变更为过期流程-机构-撞库成功
            ProductApplyFlowPO findOrgDataChangeSuc = CollUtils.findOne(listFlowByClientId, t -> Eq.objectOr(t.getStatus(), ProductApplyFlowStatusEnum.DATA_EXCHANGE_SUC.getType()));
            if (ValidParam.isNotNull(findOrgDataChangeSuc)) {
                findOrgDataChangeSuc.setUpdateTime(updateTime);
                findOrgDataChangeSuc.setStatus(ProductApplyFlowStatusEnum.EXPIRE_DATA_EXCHANGE_SUC.getType());
                productApplyFlowService.updateFlowStatusById(findOrgDataChangeSuc);
            }
            //  标识作用为插入新匹配计划
            existFlow = false;
        }

        //  流程插入-返回渠道流程
        if (!existFlow) {
            ProductApplyFlowPO channelDataExchangeFlow = ApiFlowLogger.createFlow(apiKeyPO, finalMatchPlanPO, checkMark, city, ProductApplyFlowStatusEnum.CHANNEL_DATA_EXCHANGE);
            productApplyFlowService.insert(channelDataExchangeFlow, false);
        }
    }

    /**
     *  手机号掩码模式
     *  检测当前渠道与匹配计划是否一致性
     *  如果当前渠道匹配的下游与上次不一样则将原先匹配到的流程数据放弃，并插入新流程数据
     *  如果匹配一致则无须插入流程记录
     *
     *  @param  apiParameterBO      API业务参数实体
     *  @param  checkMark           检测流程标识(PhoneMd5,或者掩码md5)
     *  @return boolean             返回是否插入新的流程标识
     */
    public void checkPhoneMaskMatchConsistency(ApiParameterBO apiParameterBO, String checkMark) {
        ApiKeyPO apiKeyPO = apiParameterBO.getApiKeyPO();

        ReqApiParameterVO reqApiParameterVO = apiParameterBO.getReqApiParameterVO();
        ApiUserRequest apiUserRequest = reqApiParameterVO.getApiUserRequest();
        String city = apiUserRequest.getCity();

        //  流程插入-返回渠道流程
        ProductApplyFlowPO channelDataExchangeFlow = ApiFlowLogger.createPhoneMaskFlow(apiKeyPO, checkMark, city, ProductApplyFlowStatusEnum.CHANNEL_DATA_EXCHANGE);
        productApplyFlowService.insert(channelDataExchangeFlow, false);
    }
}
